package com.jieli.bt.att.ui.device

import android.Manifest
import android.app.Activity
import android.bluetooth.BluetoothProfile
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.jieli.bt.att.R
import com.jieli.bt.att.data.device.DeviceConnection
import com.jieli.bt.att.databinding.FragmentDeviceBinding
import com.jieli.bt.att.ui.common.basic.BasicFragment
import com.jieli.bt.att.ui.device.details.DeviceDetailsFragment
import com.jieli.bt.att.ui.widget.dialog.TipsDialog
import com.jieli.bt.att.util.BluetoothUtil
import com.jieli.bt.att.util.PermissionUtil
import com.jieli.bt.att.util.gone
import com.jieli.bt.att.util.show
import com.jieli.logcat.JL_Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.OnPermissionDenied
import permissions.dispatcher.OnShowRationale
import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.PermissionUtils
import permissions.dispatcher.RuntimePermissions

/**
 * DeviceFragment
 * @author zqjasonZhong
 * @since 2025/3/27
 * @email zhongzhuocheng@zh-jieli.com
 * @desc 设备管理页面
 */
@RuntimePermissions
class DeviceFragment : BasicFragment() {

    companion object {
        fun newInstance() = DeviceFragment()

        /**
         * 搜索设备超时
         */
        const val ERR_SCAN_TIMEOUT = 0x1010

        /**
         * 缺少权限
         */
        const val ERR_LACK_PERMISSION = 0x1011

        /**
         * 搜索设备失败
         */
        const val ERR_SCAN_FAILED = 0x1012

        /**
         * 蓝牙未打开
         */
        const val ERR_BT_CLOSE = 0x1013

        /**
         * 位置服务未打开
         */
        const val ERR_GPS_SERVICE_CLOSE = 0x1014
    }

    private val viewModel: DeviceViewModel by viewModels()
    private lateinit var binding: FragmentDeviceBinding

    private lateinit var scanDeviceAdapter: ScanDeviceAdapter
    private lateinit var connectedDeviceAdapter: ConnectedDeviceAdapter

    private var tipsDialog: TipsDialog? = null

    private val enableBtLauncher: ActivityResultLauncher<Intent> =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {

        }
    private val enableGPSLauncher: ActivityResultLauncher<Intent> =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            tryToScanDevice()
        }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        FragmentDeviceBinding.inflate(inflater, container, false).also {
            binding = it
            return it.root
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        permissionHandler = {
            if (it.resultCode == Activity.RESULT_OK) {
                tryToScanDevice()
            }
        }
        initUI()
        addObserver()
    }

    @Suppress("DEPRECATION")
    @Deprecated("Deprecated in Java")
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        onRequestPermissionsResult(requestCode, grantResults)
    }

    @RequiresApi(Build.VERSION_CODES.S)
    @NeedsPermission(value = [Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT])
    fun onBtPermissionGrant() {
        dismissPermissionTipsDialog()
        scanDevice()
    }

    @RequiresApi(Build.VERSION_CODES.S)
    @OnShowRationale(value = [Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT])
    fun onBtPermissionShowRationale(request: PermissionRequest) {
        request.proceed()
    }

    @RequiresApi(Build.VERSION_CODES.S)
    @OnPermissionDenied(value = [Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT])
    fun onBtPermissionDenied() {
        dismissPermissionTipsDialog()
        handleStopScan(ERR_LACK_PERMISSION)
    }

    @NeedsPermission(value = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
    fun onLocationPermissionGrant() {
        checkBtPermission(true)
    }

    @OnShowRationale(value = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
    fun onLocationPermissionShowRationale(request: PermissionRequest) {
        request.proceed()
    }

    @OnPermissionDenied(value = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
    fun onLocationPermissionDenied() {
        JL_Log.i(
            TAG,
            "onLocationPermissionDenied",
            "ACCESS_COARSE_LOCATION : ${
                PermissionUtils.hasSelfPermissions(
                    requireContext(),
                    Manifest.permission.ACCESS_COARSE_LOCATION
                )
            }, " +
                    "ACCESS_FINE_LOCATION : ${
                        PermissionUtils.hasSelfPermissions(
                            requireContext(),
                            Manifest.permission.ACCESS_FINE_LOCATION
                        )
                    } "
        )
        checkBtPermission(false)
    }

    override fun onResume() {
        super.onResume()
        tryToScanDevice(false)
    }

    override fun onPause() {
        super.onPause()
        if (viewModel.isScanning()) {
            viewModel.stopScan()
        }
    }

    private fun initUI() {
        binding.viewTopBar.tvTitle.text = getString(R.string.tab_device)

        binding.viewNoneDevice.btnRetry.setOnClickListener {
            if (it.tag is Int) {
                when (it.tag) {
                    ERR_LACK_PERMISSION -> {
                        goToAppDetailsSettings()
                    }

                    ERR_BT_CLOSE -> {
                        goToBluetoothSettings()
                    }

                    ERR_GPS_SERVICE_CLOSE -> {
                        goToGpsServiceSettings()
                    }

                    else -> tryToScanDevice()
                }
            }
        }

        scanDeviceAdapter = ScanDeviceAdapter()
        scanDeviceAdapter.setOnItemClickListener { _, _, position ->
            val scanInfo = scanDeviceAdapter.getItem(position)
            if (scanInfo.connection != BluetoothProfile.STATE_DISCONNECTED) return@setOnItemClickListener
            viewModel.stopScan()
            viewModel.connectATTDevice(scanInfo)
        }
        binding.rvConnectableDevices.adapter = scanDeviceAdapter
        binding.srlDevices.setOnRefreshListener {
            lifecycleScope.launch {
                delay(800)
                if (viewModel.isScanning()) {
                    viewModel.stopScan()
                }
                delay(100)
                tryToScanDevice()
                lifecycleScope.launch(Dispatchers.Main) {
                    binding.srlDevices.isRefreshing = false
                }
            }
        }

        connectedDeviceAdapter = ConnectedDeviceAdapter()
        connectedDeviceAdapter.setOnItemClickListener { _, _, position ->
            val item = connectedDeviceAdapter.getItem(position)
            if (item.state == BluetoothProfile.STATE_CONNECTED) {
                TipsDialog.Builder().content(
                    getString(
                        R.string.disconnect_device_tips,
                        BluetoothUtil.getDeviceName(item.device)
                    )
                )
                    .cancelBtn(listener = { dialog, _ ->
                        dialog.dismiss()
                    })
                    .confirmBtn(listener = { dialog, _ ->
                        dialog.dismiss()
                        viewModel.disconnectDevice(item.device)
                    }).apply {
                        contentStyle?.color = R.color.text_color
                        cancelable = false
                    }
                    .build().show(childFragmentManager, "Disconnect_Tips_Dialog")
            }
        }
        connectedDeviceAdapter.setOnItemChildClickListener { _, _, position ->
            val item = connectedDeviceAdapter.getItem(position)
            if (item.state == BluetoothProfile.STATE_CONNECTED) {
                DeviceDetailsFragment.startActivity(requireContext(), item.device)
            }
        }
        binding.rvMyDevices.adapter = connectedDeviceAdapter
    }

    private fun addObserver() {
        viewModel.btStateMLD.observe(viewLifecycleOwner) {
            if (!it) {
                connectedDeviceAdapter.setList(mutableListOf())
                binding.groupMyDevices.gone()
                binding.aivScanning.hide()
                binding.aivScanning.gone()
                scanDeviceAdapter.setList(mutableListOf())
                handleStopScan(ERR_BT_CLOSE)
            } else {
                dismissTipsDialog()
                tryToScanDevice(false)
            }
        }
        viewModel.scanStateMLD.observe(viewLifecycleOwner) {
            handleScanState(it)
        }
        viewModel.foundDeviceMLD.observe(viewLifecycleOwner) {
            scanDeviceAdapter.addData(it)
        }
        viewModel.deviceStateMLD.observe(viewLifecycleOwner) {
            if (it.state == BluetoothProfile.STATE_CONNECTED || it.state == BluetoothProfile.STATE_DISCONNECTED) {
                updateConnectedDevicesUI()
            }
            scanDeviceAdapter.updateItem(it)
        }
        viewModel.opResultMLD.observe(viewLifecycleOwner) {
            if (it.isSuccess()) return@observe
            when (it.op) {
                DeviceViewModel.OP_SCAN_DEVICE -> handleStopScan(ERR_SCAN_FAILED)
                DeviceViewModel.OP_CONNECT_DEVICE -> {
                    if (it.data is String) {
                        showTips(getString(R.string.connect_device_fail, it.data))
                    }
                }
            }
        }
    }

    private fun checkBtPermission(result: Boolean) {
        dismissPermissionTipsDialog()
        if (result) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                if (!PermissionUtil.hasBluetoothScanPermission(requireContext()) || !PermissionUtil.hasBluetoothConnectPermission(
                        requireContext()
                    )
                ) {
                    showPermissionTipsDialog(getString(R.string.bt_permission_tips))
                }
                onBtPermissionGrantWithPermissionCheck()
                return
            }
            scanDevice()
        } else {
            handleStopScan(ERR_LACK_PERMISSION)
        }
    }

    private fun tryToScanDevice(isRequestPermission: Boolean = true) {
        if (PermissionUtil.hasBluetoothScanPermission(requireContext())
            && PermissionUtil.hasBluetoothConnectPermission(requireContext())
        ) {
            if (PermissionUtil.isHasLocationPermission(requireContext())) {
                scanDevice()
                return
            }
        }
        if (isRequestPermission) {
            if (!PermissionUtil.isHasLocationPermission(requireContext())) {
                showPermissionTipsDialog(getString(R.string.location_permission_tips))
            }
            //请求位置权限
            onLocationPermissionGrantWithPermissionCheck()
        }
    }

    private fun scanDevice() {
        if (!BluetoothUtil.isBluetoothEnable()) {
            showTipsDialog(getString(R.string.bt_close_tips)) {
                goToBluetoothSettings()
            }
            handleStopScan(ERR_BT_CLOSE)
            return
        }
        if (!PermissionUtil.isLocationServiceEnabled(requireContext())) {
            showTipsDialog(getString(R.string.location_service_close_tips)) {
                goToGpsServiceSettings()
            }
            handleStopScan(ERR_GPS_SERVICE_CLOSE)
            return
        }
        if (viewModel.isScanning()) {
            JL_Log.d(TAG, "scanDevice", "isScanning : true")
            return
        }
        viewModel.startScan()
    }

    private fun showTipsDialog(content: String, sureCallback: () -> Unit) {
        dismissTipsDialog()
        TipsDialog.Builder().title(getString(R.string.tips))
            .content(content)
            .cancelBtn(listener = { dialog, _ ->
                dialog.dismiss()
            })
            .confirmBtn(getString(R.string.open_settings), listener = { dialog, _ ->
                dialog.dismiss()
                sureCallback()
            }).also {
                it.cancelable = false
            }.build().also {
                tipsDialog = it
                it.show(childFragmentManager, TipsDialog::class.simpleName)
            }
    }

    private fun dismissTipsDialog() {
        tipsDialog?.let {
            if (it.isShow) {
                it.dismiss()
            }
        }
        tipsDialog = null
    }

    private fun goToBluetoothSettings() {
        enableBtLauncher.launch(Intent(Settings.ACTION_BLUETOOTH_SETTINGS))
    }

    private fun goToGpsServiceSettings() {
        enableGPSLauncher.launch(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
    }

    private fun handleScanState(isScan: Boolean) {
        if (isScan) {
            binding.viewNoneDevice.root.gone()
            binding.srlDevices.show()
            binding.aivScanning.visibility = View.VISIBLE
            binding.aivScanning.show()
            scanDeviceAdapter.setList(mutableListOf())
        } else {
            binding.aivScanning.hide()
            binding.aivScanning.gone()
            if (BluetoothUtil.isBluetoothEnable() && scanDeviceAdapter.data.isEmpty()) {
                handleStopScan(ERR_SCAN_TIMEOUT)
            }
        }
    }

    private fun handleStopScan(code: Int) {
        //showTips(message)
        val errString = when (code) {
            ERR_SCAN_TIMEOUT -> getString(R.string.not_found_device)
            ERR_SCAN_FAILED -> getString(R.string.failed_to_scan_device)
            ERR_LACK_PERMISSION -> getString(R.string.missing_permission)
            ERR_BT_CLOSE -> getString(R.string.bt_close)
            ERR_GPS_SERVICE_CLOSE -> getString(R.string.location_service_close)
            else -> getString(R.string.scan_device_error)
        }
        val desc = when (code) {
            ERR_SCAN_TIMEOUT -> getString(R.string.not_found_device_tips)
            ERR_SCAN_FAILED -> getString(R.string.failed_to_scan_device_tips)
            ERR_LACK_PERMISSION -> getString(R.string.missing_permission_desc)
            ERR_GPS_SERVICE_CLOSE -> getString(R.string.location_service_close_desc)
            else -> ""
        }
        binding.srlDevices.gone()
        binding.viewNoneDevice.root.show()
        binding.viewNoneDevice.tvReason.text = errString
        binding.viewNoneDevice.tvDesc.text = desc
        binding.viewNoneDevice.btnRetry.apply {
            text =
                if (code == ERR_LACK_PERMISSION || code == ERR_BT_CLOSE || code == ERR_GPS_SERVICE_CLOSE) {
                    getString(R.string.open_settings)
                } else {
                    getString(R.string.search_again)
                }
            tag = code
        }
    }

    private fun updateConnectedDevicesUI() {
        val devices = viewModel.getConnectedDevices()
            .map { DeviceConnection(it, BluetoothProfile.STATE_CONNECTED) }
        if (devices.isEmpty()) {
            binding.groupMyDevices.gone()
        } else {
            binding.groupMyDevices.show()
        }
        connectedDeviceAdapter.setList(devices)
    }
}